#pragma once
#include <stdint.h>
#include <assert.h>
#include <algorithm>
#include <functional>
#include "msg.h"

namespace acl
{
namespace mqtt
{
class packet_parser
{
public:
	packet_parser ()
	{
	}

	packet_parser (packet_parser && _packet_parser)
	{
		operator=(std::move (_packet_parser));
	}
			
	packet_parser &operator= (packet_parser && _packet_parser)
	{
		if (buffer_) {
			delete []buffer_;
		}
		buffer_ = _packet_parser.buffer_;
		_packet_parser.buffer_ = nullptr;

		buffer_pos_ = _packet_parser.buffer_pos_;
		buffer_len_ = _packet_parser.buffer_len_;
		variant32_shift_ = _packet_parser.variant32_shift_;
		fixed_header_ = _packet_parser.fixed_header_;
		status_ = _packet_parser.status_;

		_packet_parser.reset ();
		return *this;
	}
	~packet_parser ()
	{
		if (buffer_)
			delete buffer_;
	}

	bool do_parse (const char *data, int len);

	template<class MsgHandler>
	packet_parser &bind_connect_msg_handler(MsgHandler handle)
	{
		connect_msg_handler_ = handle;
		return *this;
	}
	template<class MsgHandler>
	packet_parser &bind_connect_ack_msg_handler(MsgHandler handle)
	{
		connect_ack_msg_handler_ = handle;
		return *this;
	}
	template<class MsgHandler>
	packet_parser &bind_publish_msg_handler(MsgHandler handle)
	{
		publish_msg_handler_ = handle;
		return *this;
	}

	template<class MsgHandler>
	packet_parser &bind_publish_ack_msg_handler(MsgHandler handle)
	{
		publish_ack_msg_handler_ = handle;
		return *this;
	}

	template<class MsgHandler>
	packet_parser &bind_publish_rec_msg_handler(MsgHandler handle)
	{
		publish_rec_msg_handler_ = handle;
		return *this;
	}

	template<class MsgHandler>
	packet_parser &bind_publish_rel_msg_handler(MsgHandler handle)
	{
		publish_rel_msg_handler_ = handle;
		return *this;
	}

	template<class MsgHandler>
	packet_parser &bind_publish_comp_msg_handler(MsgHandler handle)
	{
		publish_comp_msg_handler_ = handle;
		return *this;
	}

	template<class MsgHandler>
	packet_parser &bind_subscribe_msg_handler(MsgHandler handle)
	{
		subscribe_msg_handler_ = handle;
		return *this;
	}

	template<class MsgHandler>
	packet_parser &bind_subscribe_ack_msg_handler(MsgHandler handle)
	{
		subscribe_ack_msg_handler_ = handle;
		return *this;
	}

	template<class MsgHandler>
	packet_parser &bind_unsubscribe_msg_handler(MsgHandler handle)
	{
		unsubscribe_msg_handler_ = handle;
		return *this;
	}

	template<class MsgHandler>
	packet_parser &bind_unsubscribe_ack_msg_handler(MsgHandler handle)
	{
		unsubscribe_ack_msg_handler_ = handle;
		return *this;
	}

	template<class MsgHandler>
	packet_parser &bind_ping_req_msg_handler(MsgHandler handle)
	{
		ping_req_msg_handler_ = handle;
		return *this;
	}

	template<class MsgHandler>
	packet_parser &bind_ping_rsp_msg_handler(MsgHandler handle)
	{
		ping_rsp_msg_handler_ = handle;
		return *this;
	}

	template<class MsgHandler>
	packet_parser &bind_disconnect_msg_handler(MsgHandler handle)
	{
		disconnect_msg_handler_ = handle;
		return *this;
	}

	void reset ();
private:
	packet_parser (const packet_parser &)
	{

	}
	packet_parser &operator= (const packet_parser &)
	{
		return *this;
	}

	uint16_t ntoh16 (const char *buffer)
	{
		return (((uint16_t)buffer[0]) << 8) |
			((uint16_t)buffer[1]);
	}

	int parse_fixed_header (const char * data, int len);

	int store_remianing_data (const char *data, int len);

	void reset_buffer_status ();

	bool parse_msg ();

	bool parse_connect_msg ();

	bool parse_connect_ack_msg ();

	bool parse_publish_msg ();

	bool parse_publish_ack_msg ();

	bool parse_publish_rec_msg ();

	bool parse_publish_rel_msg ();

	bool parse_pushlish_comp_msg();

	bool parse_subscribe_msg();

	bool parse_subscribe_ack_msg();

	bool parse_unsubscribe_msg();

	bool parse_unsubscribe_ack_msg();

	bool parse_ping_req_msg();

	bool parse_ping_rsp_msg();

	bool parse_disconnect_msg();

private:
	std::function<bool(disconnect_msg &&)>		disconnect_msg_handler_;
	std::function<bool(ping_rsp_msg &&)>		ping_rsp_msg_handler_;
	std::function<bool(ping_req_msg &&)>		ping_req_msg_handler_;
	std::function<bool(unsubscribe_ack_msg &&)> unsubscribe_ack_msg_handler_;
	std::function<bool(unsubscribe_msg &&)>		unsubscribe_msg_handler_;
	std::function<bool(subscribe_ack_msg &&)>	subscribe_ack_msg_handler_;
	std::function<bool(subscribe_msg &&)>		subscribe_msg_handler_;
	std::function<bool (publish_comp_msg &&)>	publish_comp_msg_handler_;
	std::function<bool (publish_rel_msg &&)>	publish_rel_msg_handler_;
	std::function<bool (publish_rec_msg &&)>	publish_rec_msg_handler_;
	std::function<bool (publish_msg&&)>			publish_msg_handler_;
	std::function<bool (publish_ack_msg &&)>	publish_ack_msg_handler_;
	std::function<bool (connect_msg &&)>		connect_msg_handler_;
	std::function<bool (connect_ack_msg &&)>	connect_ack_msg_handler_;

	fixed_header fixed_header_;
	char *buffer_ = nullptr;
	int buffer_pos_= 0;
	int buffer_len_ = 0;

	enum status_t
	{
		e_fixed_header_type,
		e_fixed_header_remaining_length,
		e_get_remaining_data,
		e_get_remaining_data_done,
	};
	int variant32_shift_ = 0; //
	status_t status_ = e_fixed_header_type;
};
}
}
